home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Media 22
/
PC MEDIA CD22.iso
/
share
/
prog
/
datalib2
/
dataexe1.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-15
|
17KB
|
662 lines
/*
INTRODUCTION
This is a generic expression evaluator which is intended to be used
by any program which requires one.
It is intended to be expandable and allows the use of variables by user
written routines which can be modified according to the specific program
being used. An error routine may also be specified.
Users are expected to call two routines externally, the first is the
initialisation routine which reads a data file (which may form part of
a larger data file from the external program) and which should only be
called once, the second is the expression evaluator which takes a string
and returns an integer, it will also update the string so that the first
character not forming part of the expression is returned.
Strings may be passed in non-tokenised form which is slow, or may be
tokenised before being passed which results in faster evaluation. There
is a tokeniser routine which may be called externally to tokenise
expressions in loaders or compilers etc.
The evaluator allows the use of functions, constants, and unary and binary
operators.
ROUTINES to be used are as follows:
exeinit initialises the evaluator, reads operator data file,
grabs memory as required.
exetoken Takes an ascii string and produces a tokenised output,
it is not essential to tokenise strings as the evaluator can
do it for you.
exeval Evaluate an expression. Pass a string pointer to a pointer
containing the expression and the result is returned as
an int. The string pointer is updated to point to the first
character after the expression whether it is tokenised or
not.
The routine returns a pointer to an operand structure which
indicates the type of return (string or integer), and contains
the return value, which in the case of a string is the pointer
to it.
ROUTINES to be provided by the user
void errfatal(char *)
fatal error handler must be written by the user, and must
exit the program printing the supplied string.
void err(char *)
error handler, non fatal, must be written by the user, and
return to the calling routine.
struct op *getvar(char *)
Get a variable pointed to by the string and return its value,
getvar should contain a local static op structure whose
address is returned.
EXPANDING the program
The program may be expanded or modified to handle different functions or
existing functions in a different way.
Constants may be added without changing the program at all, simply changing
EXOP.DAT
EXOP.DAT which may be incorporated in a user data file must be modified,
the information contained in it is the string, priority, type and number
of each operator. The number of the operator may be substituted below.
Operators and functions are dealt with in the body of the evalstr routine.
*/
#include "datapriv.hpp"
/*
This file is the data required for the expression evaluator.
*/
/*
This file contains data in the form
LITERAL,TYPE,PRIORITY,TYPE of 1st OP,TYPE of 2nd OP,Type Result,No.
Type is : 0, Constant (pri=value)
1, Unary operator
2, Binary Operator
3, Both types (e.g. -)
>=4, Function, PRI=no. of parameters
Note for a function the type represents the number of optional
parameters, so 1 optional parameter has a type of 5, optional
parameters with no value supplied are inserted as integer -1.
Type-1 & Type-2 are the types of operand, 1=num, 2=string, 4=Date, 8=Logical
LIT TYPE PRI Type-1 Type-2 Type-Result
*/
struct opinf oppt[]=
{
"(",4,9,0,0,0,0,
")",4,9,0,0,0,1,
"+",3,5,7,7,1,2,
"-",3,5,7,7,1,3,
"**",2,7,1,1,1,4,
"*",2,6,1,1,1,5,
"/",2,6,1,1,1,6,
".AND.",2,2,8,8,8,7,
".OR.",2,1,8,8,8,8,
".NOT.",1,3,8,8,8,9,
"=",2,4,15,15,8,10,
"<>",2,4,15,15,8,11,
">=",2,4,7,7,8,12,
"<=",2,4,7,7,8,13,
">",2,4,7,7,8,14,
"<",2,4,7,7,8,15,
"$",2,4,2,2,8,16,
"ABS",4,1,1,0,1,17,
"ASC",4,1,2,0,1,18,
"AT",4,2,2,2,1,19,
"CDOW",4,1,4,0,2,20,
"CHR",4,1,1,0,2,21,
"CMONTH",4,1,4,0,2,22,
"CTOD",4,1,2,0,4,23,
"DATE",4,0,0,0,4,24,
"DAY",4,1,4,0,1,25,
"DOW",4,1,4,0,1,26,
"DTOC",4,1,4,0,2,27,
"DTOS",4,1,4,0,2,28,
"IIF",4,3,8,15,0,29,
"INT",4,1,1,0,1,30,
"ISALPHA",4,1,2,0,8,31,
"ISDIGIT",4,1,2,0,8,32,
"ISLOWER",4,1,2,0,8,33,
"ISUPPER",4,1,2,0,8,34,
"LEFT",4,2,2,1,2,35,
"LEN",5,1,2,0,1,36,
"LOWER",4,1,2,0,2,37,
"LTRIM",4,1,2,0,2,38,
"MAX",4,2,5,5,0,39,
"MIN",4,2,5,5,0,40,
"MOD",4,2,1,1,1,41,
"MONTH",4,1,4,0,1,42,
"RECCOUNT",4,0,0,0,1,43,
"RECNO",4,0,0,0,1,44,
"RECSIZE",4,0,0,0,1,45,
"REPLICATE",4,2,2,1,2,46,
"RIGHT",4,2,2,1,2,47,
"ROUND",4,2,1,1,1,48,
"RTRIM",4,1,2,0,2,49,
"SOUNDEX",4,1,2,0,2,50,
"SPACE",4,1,1,0,2,51,
"STR",6,1,1,1,2,52,
"STUFF",4,4,2,1,2,53,
"SUBSTR",5,2,2,1,2,54,
"SWAPDATA",4,1,2,0,2,55,
"TIME",4,0,0,0,2,56,
"TRIM",4,1,2,0,2,57,
"TYPE",4,1,15,0,0,58,
"UPPER",4,1,2,0,2,59,
"VAL",4,1,2,0,1,60,
"YEAR",4,1,4,0,1,61,
"^",2,7,1,1,1,62,
"#",2,4,15,15,8,63
};
// Number of operators
int nop=64;
// Lookup table for end of array and spaces
// bit 0=space, bit 1=end of expr.
// End chars are 0 \n , : ; ) '
// Space chars are 32 and 9
char sparray[]=
{
// 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
2,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00-1f
1,0,0,0,0,0,0,2,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0, // 20-3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40-5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 60-7f
};
/**************************************************************************
Initialisation routine, this takes a file pointer and loads the file of
operator definitions. This file is by default included in EXOP.DAT.
The format of the file is as follows:
#
STRING,TYPE,PRIORITY,TYPE1,TYPE2,NUMBER
STRING,TYPE,PRIORITY,TYPE1,TYPE2,NUMBER
.
.
.
#
Characters up to the first '#' are ignored, there should be no spurious
spaces or tabs within the data.
STRING is the literal for the operator, e.g. +,-,AND.
TYPE is the type, 0=constant, 1=Unary op, 2=Binary op.,
3=binary or unary op, 4=function.
PRIORITY for a BINARY operator is the priority, higher number=higher priority
(the priority must be greater than 0).
for a CONSTANT this number is the value of the constant.
for a FUNCTION this number is the number of arguments.
TYPE1 is the type of the single operand for functions and unary operators
and the type of first operand (the left operand) for binary. Type
is 1 for integer, 2 for string or 3 for either.
TYPE2 only has meaning for binary operators where it is the type of the
second operand (after the operator).
NUMBER is the index of the operator and should be used by the tokeniser.
This also initialises SPARRAY, which contains special character information
as follows:
If a character (n) is not special then sparry[n]=0;
Bit 0 of sparray[n]=space character.
Bit 1 of sparray[n]=end of expression character.
***************************************************************************/
expval::expval(database *dbi)
{
erflag=0;
*(dbi->ers)=0;
db=dbi;
if (!(exwork=new char[EXWORKSIZE]))
errfatal("No memory for expression evaluator information");
}
// Destructor
expval::~expval()
{
if (exwork) delete exwork;
}
/**************************************************************************
Tokeniser
This routine tokenises an input string and passes the result to an output
string, it is useful for input scanners, but is also used by the expression
evaluator when an untokenised expression is passed.
The tokenised expression consists of a number of fields each of which starts
with a byte:
EXSTART Indicates the 1st byte of the expression
EXINT Indicates an integer follows in 2 bytes.
EXVAR Indicates a variable, followed by the variable name which is
terminated by a 0 byte.
EXSTR A zero terminated string.
EXEND Last byte of expression.
>=0 Indicates an operator, consists of the operator index.
It speeds up the evaluator by pre-evaluating constants and integers.
It should be passed two strings : s1 and s2
s1 is a pointer to the input string
s2 is a pointer to the output string which the tokenised result is written to
The routine returns a pointer to the 1st character which is not in the
expression. The expression is taken to end with one of the four EXEND
characters defined in the header file.
**************************************************************************/
char *expval::exetoken(char *s1,char **s2)
{
char *ls1=s1,*ls2=*s2; // Pointer to next free byte in s2
char ws[128],*wsp; // Useful space
int c,i;
*ls2++=EXSTART; /* Write in the expression flag */
c=exescan(&ls1); // Get next character
do
{
if (isdigit(c) || c==34 || (c=='.' && isdigit(*ls1))) // Number/String ?
{
if (c==34) /* A string */
{
*ls2++=EXSTR; c=exescan(&ls1,1);
while(c!=34 && c) {*ls2++=c; c=exescan(&ls1,1);} *ls2++=0;
if (c==34) c=exescan(&ls1);
}
else /* A number */
{
wsp=ws;
while(isdigit(c) || (c=='.'&& isdigit(*ls1))) {*wsp++=c; c=exescan(&ls1);}
*wsp=0; *ls2++=EXINT; (*(double *)ls2)=atof(ws); ls2+=sizeof(double);
}
}
else /* expression or a variable */
{
if ((i=exegetop(c,&ls1))!=-1) /* Have we found an expression */
{
if (i==OBRAC)
{
ls1=exetoken(ls1,&ls2);
if (*ls1!=')') {err("Unmatched Bracket !"); c=0;}
else {ls1++; c=exescan(&ls1);}
}
else // Operator or function, not an open bracket
{
c=exescan(&ls1);
if (oppt[i].type==CONSTANT)
{*ls2++=EXINT; (*(double *)ls2)=oppt[i].pri; ls2+=sizeof(double);}
else
{
*ls2++=i; // Some kind of operator
if (oppt[i].type>=FUNCTION) // A function
{
if (c!='(') {err("Need a \'(\' for a function"); c=0;}
else
{
for(int j=oppt[i].pri; j>0; j--) // Get n-1 parameters, ending in ,
{
ls1=exetoken(ls1,&ls2);
if (j>1) // expect , between parameters
{
exescan(&ls1);
if (*ls1!=',') {err("Expected a \',\'"); c=0; j=0;}
else ls1++;
}
}
for(j=0; j<oppt[i].type-4; j++) // Optional parameters
{
if (*ls1==',') {ls1++; ls1=exetoken(ls1,&ls2);}
else {*ls2++=EXSTART; *ls2++=EXINT; (*(double *)ls2)=-1;
ls2+=sizeof(double); *ls2++=EXEND;}
}
exescan(&ls1);
if (*ls1!=')') {err("Needs a \')\' !"); c=0;}
else {ls1++; c=exescan(&ls1);}
}
}
}
}
}
else /* No expression, field ? */
{
if (isalpha(c))
{
field *f;
wsp=ws;
while(isalpha(c) || c=='_') {*wsp++=c; c=exescan(&ls1);} *wsp++=0; /* field */
f=db->getfield(ws);
if (!f) {err("Invalid field"); c=0;}
else {*ls2++=EXVAR; *(int *)ls2=f->number; ls2+=sizeof(int);}
}
else
{err("Unrecognised Expression"); c=0;}
}
}
}
while(c);
*ls2++=EXEND;
*s2=ls2;
return(ls1);
}
// Scan input line up to next space, or end of line
// sflag is 1 if we are reading within inverted commas ("")
int expval::exescan(char **in,int sflag)
{
int c;
if (sflag) {if (**in=='\n' || !(**in)) return 0;}
else {if (sparray[**in] & 2) return 0;}
if (sflag) c=**in;
else
{
while(sparray[**in] & 1) (*in)++; /* Eliminate leading spaces */
c=(islower(**in)) ? **in-('a'-'A'): **in;
}
(*in)++;
return c;
}
/* Check supplied string to see if it is an operator */
// c is the first character
int expval::exegetop(int c,char **s)
{
char ws[128],*wsp;
char *ls=*s;
wsp=ws;
*wsp++=c;
for(int i=1; i<MAXOPLEN; i++) ws[i]=exescan(&ls);
struct opinf *lop; /* Local pointer */
lop=oppt;
for(i=0; i<nop; i++)
{
if (!strncmp(lop->lit,ws,strlen(lop->lit)))
{*s+=strlen(lop->lit)-1; return(lop->num);}
lop++;
}
return(-1);
}
/*
This routine grabs size characters from the expression evaluator
work space.
*/
char *expval::exealloc(int size)
{
char *temp;
temp=exworkp; exworkp+=size;
if (exworkp>exwork+EXWORKSIZE-40) errfatal("Out of expression evaluator space");
return(temp);
}
/*
This is a properly implemented input routine
s is the input buffer, n is the maximum number of characters.
It returns the number of characters read.
*/
int expval::exeinput(char *s,int n)
{
char *wsp,c;
wsp=s;
c=getch();
while(c!=13)
{
if (!c) getch();
else
{
if ((c==8) && (wsp!=s)) {wsp--; printf("\x08 \x08");}
else if (c!=8 && (wsp-s<n)) {*wsp++=c; printf("%c",c);}
}
c=getch();
}
*wsp=0;
return(wsp-s);
}
/***********************************************************************
Comparison Routines
************************************************************************/
void expval::execomp(op *fo,op *so,int type)
{
if (fo->optype!=so->optype) {err("Data types should match"); return;}
float x;
if (fo->optype==OPDATE)
{
x=atof(fo->date); fo->num=x; x=atof(so->date); so->num=x;
}
if (fo->optype==OPSTR) x=strcmpdb(fo->str,so->str);
else x=fo->num-so->num;
switch(type)
{
case NE2 :
case NE : fo->num=x; break;
case EQUAL : fo->num=!x; break;
case GT : fo->num=(x>0); break;
case LT : fo->num=(x<0); break;
case GTE : fo->num=(x>=0); break;
case LTE : fo->num=(x<=0); break;
}
if (fo->num) fo->num=-1;
fo->optype=OPLOG;
}
/*************************************************************************
DATE Functions
**************************************************************************/
// Get day of week (character)
char *explitday[]={"Sunday","Monday","Tuesday","Wednesday",
"Thursday","Friday","Saturday"};
char *expval::cdow(char *date) {return explitday[dow(date)-1];}
// Get day of week (numeric)
int expval::dow(char *date) {return((long)(days(date)+5-DATEOFF)%7);}
// Get month (character)
char *explitmon[]={"January","February","March","April","May","June","July",
"August","September","October","November","December"};
char *expval::cmonth(char *date)
{
int x,y,z;
gnums(date,x,y,z);
return(explitmon[y-1]);
}
// Days since year 0 + dBase offset
int expvaldm[]={0,31,59,90,120,151,182,213,243,274,304,334};
long expval::days(char *date)
{
int d,m,y;
gnums(date,d,m,y);
int ly=(!(y%4) && (y%400)); // Leap year flag
return((long)d+long(expvaldm[m-1])-(long)(ly && m<3)+
(long)((long)y*1461L)/4L+DATEOFF);
}
// Convert days since year 0 into a date
void expval::daystodate(char *date,long days)
{
int d,m,y;
int dintoy;
days-=DATEOFF; // dBase offset
y=(long)((days*4L)/1461L);
int ly=(!(y%4) && (y%400)); // Leap year flag
dintoy=days-(long)((y*1461L)/4L);
m=0; while(dintoy>expvaldm[m]-(ly && m<2)) m++;
d=dintoy-expvaldm[m-1]+(ly && m<3);
sprintf(date,"%04d%02d%02d",y,m,d);
}
// Convert character string to date (eg "19/11/62" in UKDATE format)
void expval::ctod(char *date,char *s)
{
char *sp=s;
int d,m,y;
if (db->dateformat==UKDATE)
{
d=atoi(sp); while(isdigit(*sp++));
m=atoi(sp); while(isdigit(*sp++));
y=atoi(sp);
}
else
{
m=atoi(sp); while(isdigit(*sp++));
d=atoi(sp); while(isdigit(*sp++));
y=atoi(sp);
}
if (!y || !m || !d) {strcpy(date," / / "); return;}
if (!(y/100)) y+=1900;
sprintf(date,"%04d%02d%02d",y,m,d);
}
/******************** SOUNDEX *******************************************/
// Provide soundex of character
char sndx[]={0,1,2,3,0,1,2,0,0,2,2,4,5,5,0,1,2,6,2,3,0,1,0,2,0,2};
char* FD soundex(char *d,char *s)
{
char sl[128];
char *sp;
sp=strupr(strncpy(sl,s,126)); sl[127]=0;
strcpy(d,"0000");
while(isspace(*sp)) sp++;
if (!isalpha(*sp)) return d;
*d=*sp++;
for(int i=1; i<4; i++)
{
while(*sp && isalpha(*sp) && (!sndx[*sp-'A'] || *(sp+1)==*sp)) sp++;
if (!*sp || !isalpha(*sp)) break;
d[i]=sndx[(*sp++)-'A']+'0';
}
return d;
}
/******************** Error Handling ************************************/
void expval::err(char *s)
{
strcpy(db->ers,s); erflag=1;
}
void expval::errfatal(char *s)
{
fprintf(stderr,"Expression evaluator fatal error - %s",s);
erflag=2;
}